package org.alwayssuper.protocol.codec;

import org.alwayssuper.protocol.commons.GBTRequestType;
import org.alwayssuper.protocol.commons.GBTResponseType;
import org.alwayssuper.protostar.Schema;
import org.alwayssuper.protostar.SchemaManager;
import org.alwayssuper.protostar.schema.RuntimeSchema;
import org.alwayssuper.protostar.util.ArrayMap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;

import org.alwayssuper.protostar.util.Explain;
import io.netty.buffer.*;
import org.alwayssuper.protocol.basics.GBTMessage;
import org.alwayssuper.protocol.commons.JTUtils;
import org.alwayssuper.protocol.commons.VersionType;


/**
 * GBT协议解码器
 * @author alwaysSuper
 */
public class GBTMessageDecoder {
    private static final ByteBufAllocator ALLOC = PooledByteBufAllocator.DEFAULT;

    private final SchemaManager schemaManager;

    private final ArrayMap<RuntimeSchema> headerSchemaMap;

    public GBTMessageDecoder(String... basePackages) {
        this.schemaManager = new SchemaManager(basePackages);
        this.headerSchemaMap = schemaManager.getRuntimeSchema(GBTMessage.class);
    }

    public GBTMessageDecoder(SchemaManager schemaManager) {
        this.schemaManager = schemaManager;
        this.headerSchemaMap = schemaManager.getRuntimeSchema(GBTMessage.class);
    }
    public GBTMessage decode(ByteBuf input) {return decode(input, null);}

    public GBTMessage decode(ByteBuf input, Explain explain ){

        int version;
        int i = input.readShort();
        VersionType versionType = VersionType.fromProtocolVersion(i);
        version = versionType.getValue();
        boolean verified = verify(input);
        int messageId =  input.getUnsignedByte(2);
        int responseType = input.getUnsignedByte(3);
        int headLen = JTUtils.headerLength(version);
        //算上两个字节的分隔符
        headLen += 2;

        RuntimeSchema<GBTMessage> headSchema = headerSchemaMap.get(version);
        RuntimeSchema<GBTMessage> bodySchema;

        //处理32960协议消息指令下发后设备的应答时，下发应答的消息类型和服务器收到的需被应答的消息的消息类型相同的问题
        if(responseType != GBTResponseType.命令 ){
            bodySchema = schemaManager.getRuntimeSchema(GBTRequestType.应答标识, version);
        }else {
            bodySchema = schemaManager.getRuntimeSchema(messageId, version);
        }

        GBTMessage message;
        if (bodySchema == null)
            message = new GBTMessage();
        else
            message = bodySchema.newInstance();
        message.setVerified(verified);
        message.setPayload(input);
        message.setProtocolVersion(version);

        int writerIndex = input.writerIndex();
        input.writerIndex(headLen);
        headSchema.mergeFrom(input, message, explain);
        //跳过最后一位的效验
        input.writerIndex(writerIndex - 1);
        if (bodySchema != null) {
            int bodyLen = message.getPayloadLength();
            input.readerIndex(headLen);
            bodySchema.mergeFrom(input, message, explain);
        }
        return message;
    }

    /** 校验 */
    public static boolean verify(ByteBuf buf) {
        byte checkCode = JTUtils.bcc(buf, -1);
        return checkCode == buf.getByte(buf.writerIndex() - 1);
    }
}
